注意:所有文章除特别说明外,转载请注明出处.
反射
反射表示将类的属性和方法映射成相应的类。Java反射机制表示在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。对于任意一个对象都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法功能是反射。
在java.lang.reflect包下有三个类:Field类 | Method类 | Constructor类,三者分别描述了一个类的域 | 方法 | 构造函数。三个类都有一个getName()方法。Field类有一个getType()方法,返回一个Class类型对象。三个类都有一个getModifiers()方法,返回一个整数,(0|1)描述方法所使用的修饰符。
Java反射框架提供的功能
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)
4.在运行时调用任意一个对象的方法
5.注意上面的所有情况都是在运行时,而不是在编译时
Class类和Object类
1.Class是一个类,一个描述类的类,封装描述方法的Method,描述字段的Filed,描述构造器的Constructor等属性。
2.对象经过反射后可以得到的属性:某类的数据成员变量名、方法、构造器,某个类实现的接口。
3.一个Class对象包含了特定某个类的有关信息。
4.Class对象只能由系统建立对象
5.一个类在JVM中只有一个Class实例。
1.反射的基本使用
1.获取Class类的三种方法:
- 类名.class
- 对象名.getClass()
- Class.forName(“加载的类名”)
2.判断是否是某个类的实例:
通过 instanceof 关键字判断是否是某个类的实例
2.实现流程:
1.使用上面的Class类获取方法获取特定的Class类,即该类对应的字节码。
2.调用Class对象的getConstructor(Class<?>... parameterTypes)获取构造方法对象
3.调用是构造方法类Constructor的newInstance(Object... initargs)方法新建对象
4.调用Class对象的getMethod(String name, Class<?>... parameterTypes)获取方法对象
5.调用方法对象类Method的invoke(Object obj, Object... args)方法,调用对象上相应方法
注意:使用方法的参数唯一标识一个方法。依据:方法的重载。
2.枚举类成员
//案例:打印一个类的所有方法
...
Class<?> c1 = Class.forName(className);
while(c1 != null){
for(Method m : c1.getDeclaredMethods()){
System.out.println(
Modifier.toString(m.getModifiers())+""+
m.getReturnType().getCanonicalName()+""+
m.getName()+
Arrays.toString(m.getParameters())
);
}
c1 = c1.getSuperclass();
}
3.对象的检查
注意:在使用私有的Field和Method对象之前,我们必须让它们是可以访问的。调用 setAccessible(true) 方法可以为反射 解锁 方法或域访问限制。但模块系统或者安全管理器会阻止请求,并以这种方式保护对象不被访问。所以在这种情况下可以调用 trySetAccessible() 方法,如果域或者方法是不可以被访问的,则该方法返回false。
4.方法的调用
注意:即使 clone() 方法是所有数组类型的公有方法,当有描述数组类的Class对象上调用getMethod()方法时,返回的方法数字也不包含 clone() 方法。
5.对象的构造
如果我们需要构造对象,需要先找到 Constructor 对象,然后调用它的 newInstance() 方法。
Constructor const = c1.getConstructor(int.class);
Object obj = const.newInstance(42);
2.数组的反射
public class ReflectTest {
public static void main(String[] args) {
int [] a1 = new int[]{1,2,3};
int [] a2 = new int[5];
int [][] a3 = new int[2][3];
System.out.println(a1.getClass() == a2.getClass());//true
System.out.println(a1.getClass());//class [I
System.out.println(a3.getClass());//class [[I
System.out.println(a1.getClass().getSuperclass() == a3.getClass().getSuperclass());//true
System.out.println(a2.getClass().getSuperclass());//class java.lang.Object
//下句编译不通过:Error:(15, 42) java: 不可比较的类型: java.lang.Class<capture#1, 共 ? extends int[]>和java.lang.Class<capture#2, 共 ? extends int[][]>
//System.out.println(a1.getClass() == a3.getClass());
Object []b3 = a3;//通过
//下句编译不通过 Error:(17, 24) java: 不兼容的类型: int[]无法转换为java.lang.Object[]
//Object [] b1 = a1;
String s1 = "abc";
System.out.println(Arrays.asList(a1));//[[I@1540e19d]
System.out.println(Arrays.asList(s1));//[abc]
}
}
output:
true
class [I
class [[I
true
class java.lang.Object
[[I@1540e19d]
[abc]
注意:上面的例子说明,1.对于元素同类型的数组,同维数组,它们的class一样。2.当数组不同维时,它们的class不一样。3.当不同维时,父类都是Object,所以得到的结果都一样。4.基本类型一维数组不能直接转换为Object[]。
注意:HashCode与内存泄漏问题:1.hashcode一旦确定就不要变,否则容易出错。2.如果对象equals之后返回true,则它们的hashcode值相同。3.但是如果equals方法返回false,不一定表示两者的hashcode值不同。
3.配置文件的加载
类加载器加载只读配置文件
类名.class.getClassLoader().getResourceAsStream(str);类名.class.getResourceAsStream(str);是指还是调用类加载器。
//源码
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
注意:参数str的写法:1.不加斜杠,相对路径:str=”config.properties”; 2.加斜杠,从classpath的根路径找:str=”cn/edu/pku/config.properties”;
4.内省(Instropector)和JavaBean
我们使用JavaBean获取属性值的流程大致是:变大写->补前缀->获取方法。
- 1.使用内省操作
简单实现: 使用java.beans.PropertyDescriptor类
麻烦实现: 使用java.beans.Introspector类,遍历getBeanInfo方法的返回值
JavaBean必须有一个不带参数的构造函数
- 使用BeanUtils工具包
1.字符串和整数转换(对比(PropertyUtils)
2.属性级联操作
3.操作map
Method类 java.lang.reflect.Method
Method类位于java.lang.reflect.Method包下,在Java反射中Method类描述的是类的方法信息。在Method类的实例描述了方法的全部信息(如:方法修饰符、方法名称、参数列表等)。
1.获取Method类对象
1.getMethods() 获取类的public方法
2.getMethod(String name, Class[] params) 获取类的特定方法,name参数指定方法的名称,params参数指定方法的参数类型
3.getDeclaredMethods() 获取类中所有的方法(public protected default private)
4.getDeclaredMethod(String name, Class[] params) 获得类的特定方法,name参数指定方法的名字,params参数指定方法的参数类型
2.Method类常用方法
public class MethodTest{
public static void main(String[] args){
//1.获取操作类的所对应的Class对象
Class<?> class = Class.forName("com.xidian.edu.cn.entity.User");
//2.使用该类的class对象生成实例
Object obj = class.newInstance();
//3.使用方法
Method addMethod = class.getMethod("addResult", new Class[]{int.class});
...
}
}
注意:Method类的invoke(Object object, Object args[]) 方法作用是:
调用该对象描述的方法,传递给定的参数,返回被调用方法的返回值。如果是静态方法,则将null传递给object。
接收的参数必须是对象,如果参数为基本类型数据,必须转换为包装类型的对象。invoke()方法的返回值总是对象。如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
代理
Proxy 类可以在运行时创建实现了给定接口或者接口集的新类。代理类包括了特定接口所要求的所有方法,并且这些方法在Object类中定义的。调用处理器:是一个实现了InvocationHandler 接口的类对象。InvocationHandler 接口只有一个方法:
Object invoke(Object proxy, Method method, Object[] args)
创建一个代理对象使用 Proxy 类的 newProxyInstance() 方法。该方法有三个参数:1.类加载器。2.Class对象数组。3.调用处理器。